#include "robotaudioi.h"
#include "tmglobaldef.h"
#include <QDomDocument>

RobotAudioI::RobotAudioI()
{
}

void RobotAudioI::Init()
{
    if( !g_pAudioSender ){
        g_pAudioSender = new TmAudioSender();
        g_pAudioSender->Init();
    }

}

void RobotAudioI::TtsPlayParams(const std::string &strTxt, const std::string &strSpeaker, Ice::Int nSpeed, Ice::Int nPitch, const Ice::Current &)
{
    QString strTxtO = QString::fromUtf8(strTxt.c_str());
    QString strSpeakerO = QString::fromUtf8(strSpeaker.c_str());
    g_pAudioSender->TtsPlayParams(strTxtO, strSpeakerO, nSpeed, nPitch);
}

void RobotAudioI::TtsPlay(const std::string &strTxt, const Ice::Current &)
{
    QString strTxtO = QString::fromUtf8(strTxt.c_str());
    g_pAudioSender->TtsPlay(strTxtO);
}

void RobotAudioI::TtsPlayFile(const std::string &strPath, const Ice::Current &)
{
    QString strPathO = QString::fromUtf8(strPath.c_str());
    g_pAudioSender->TtsPlayFile(strPathO);
}

void RobotAudioI::TtsSetVolume(Ice::Int nVal, const Ice::Current &)
{
    g_pAudioSender->TtsSetVolume(nVal);
}

void RobotAudioI::TtsStop(const Ice::Current &)
{
    g_pAudioSender->TtsStop();
}

bool RobotAudioI::TtsIsPlaying(const Ice::Current &)
{
    return g_pAudioSender->TtsIsPlaying();
}

void RobotAudioI::AsrBuild(const std::string &strBnfText, const Ice::Current &)
{
    QString strBnfO = QString::fromUtf8(strBnfText.c_str());
    g_pAudioSender->AsrBuild(strBnfO);
}

void RobotAudioI::AsrSetGramma(const std::string &name, const Ice::Current &)
{
    QString strName = QString::fromUtf8(name.c_str());
    g_pAudioSender->AsrSetGramma(strName);
}

void RobotAudioI::AsrStart(Ice::Int nMaxDelayMs, bool bIsOffline, const IceUtil::Optional<bool> &bIsChinese, const Ice::Current &)
{
    bool bIsRealChinese = true;
    try{
        bIsRealChinese = bIsChinese.get();
    }catch(...){

    }
    g_pAudioSender->AsrStart(nMaxDelayMs,bIsOffline, bIsRealChinese);
}

void RobotAudioI::AsrStop(const Ice::Current &)
{
    g_pAudioSender->AsrStop();
}

void RobotAudioI::AsrAnswerEnable(bool bEnable, const Ice::Current &)
{
    g_pAudioSender->bAnswerEnable = bEnable;
}

void RobotAudioI::AsrSetTimeOut(Ice::Int nTimeDelayS, const Ice::Current &)
{
    g_pAudioSender->AsrSetTimeOut(nTimeDelayS);
}

void RobotAudioI::MediaPlay(const std::string &strPath, const Ice::Current &)
{
    QString strPathO = QString::fromUtf8(strPath.c_str());
    g_pAudioSender->MediaPlay(strPathO);
}

void RobotAudioI::MediaSetVolume(Ice::Int nVal, const Ice::Current &)
{
    g_pAudioSender->MediaSetVolume(nVal);
}

void RobotAudioI::MediaStop(const Ice::Current &)
{
    g_pAudioSender->MediaStop();
}

bool RobotAudioI::MediaIsPlaying(const Ice::Current &)
{
    return g_pAudioSender->MediaIsPlaying();
}

void RobotAudioI::MicReset(const Ice::Current &)
{
    if(g_bMicEnable){
        g_pAudioSender->MicReset();
    }
}

void RobotAudioI::MicSet(Ice::Int nVal, const Ice::Current &)
{
    if(g_bMicEnable){
        g_pAudioSender->MicSet(nVal);
    }
}

TmAudioWorker::TmAudioWorker()
{
    m_pMediaPlayer = NULL;
    m_pTTSPlayer = NULL;
    m_tts_volume = 100;
    m_media_volume = 100;
    m_pAsrSDK = NULL;
    m_pcm_val = 0;
    m_pAudioInput = NULL;
    m_bIsSerInit = false;
    m_bIsOffline = true;
}

void TmAudioWorker::slot_Init()
{
    if(m_pMediaPlayer||m_pTTSPlayer){
        return;
    }
    m_pMediaPlayer = new QMediaPlayer();
    m_pTTSPlayer = new QMediaPlayer();
    m_tts_speaker = g_pSettingCore->value("TTS/Speaker","xiaofeng").toString();
    m_tts_speed = g_pSettingCore->value("TTS/Speed",67).toInt();
    m_tts_pitch = g_pSettingCore->value("TTS/Pitch",70).toInt();
    m_tts_volume = g_pSettingCore->value("Volume/TTS",80).toInt();
    m_pTTSPlayer->setVolume(m_tts_volume);
    m_media_volume = g_pSettingCore->value("Volume/Media",100).toInt();
    m_pMediaPlayer->setVolume(m_media_volume);

    m_pcm_val = g_pSettingCore->value("ASR/PCM",0).toInt();
    m_strGramName = g_pSettingCore->value("ASR/Default","tami").toString();

    m_strDirTTS = QString("%1/Tmp/tts/").arg(g_strRootDir);
    QDir dirRoot(m_strDirTTS);
    if( !dirRoot.exists() ){
        if(!dirRoot.mkpath(m_strDirTTS)){
            qDebug()<<"[TTS] MakeDir failed!"<<m_strDirTTS;
        }
    }

    m_pAsrSDK = new Sharedlib_kdxf();
    if(!m_pAsrSDK->login("556d116e")){
        qDebug()<<"[Audio] libKDXF SDK login Failed!";
    }
    m_pAsrSDK->isr_setGramID(m_strGramName);
    connect(m_pAsrSDK,SIGNAL(sig_IsrResult(QString)),this,SLOT(slot_asr_over_online(QString)));


    // init audio out
    if( !m_pAudioInput ){
        QAudioFormat format;
        // Set up the desired format, for example:
        format.setSampleRate(16000);
        format.setChannelCount(1);
        format.setSampleSize(16);
        format.setCodec("audio/pcm");
        format.setByteOrder(QAudioFormat::LittleEndian);
        format.setSampleType(QAudioFormat::SignedInt);
        QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
        if (!info.isFormatSupported(format)) {
            //qWarning() << "Default format not supported, trying to use the nearest.";
            format = info.nearestFormat(format);
        }
        m_pAudioInput = new QAudioInput(format);
        m_pAudioDeviceIn = m_pAudioInput->start();
        connect(m_pAudioDeviceIn, SIGNAL(readyRead()), this, SLOT(slot_audio_data()), Qt::DirectConnection);
        qDebug()<<"m_pAudioInput success!";
    }

    if(g_bMicEnable){
        // init serial port
        m_pSerial = new QSerialPort();
        m_pSerial->setPortName(g_strAudioCOM);
        m_pSerial->setBaudRate(QSerialPort::Baud115200);
        m_pSerial->setDataBits(QSerialPort::Data8);
        m_pSerial->setStopBits(QSerialPort::OneStop);
        m_pSerial->setParity(QSerialPort::NoParity);

        m_bIsSerInit = m_pSerial->open(QIODevice::ReadWrite);

        if (!m_bIsSerInit)
        {
            qDebug() << "open serial failed!";
            return;
        }
        // recv data
        //connect(m_pSerial, &QSerialPort::readyRead, this, &TmAudioWorker::slotser_newdata, Qt::QueuedConnection);
    }

}

void TmAudioWorker::slot_TtsPlayParams(QString strTxt, QString strSpeaker, int nSpeed, int nPitch)
{
    QString strParams = QString("%1_%2_%3_%4_%5").arg(strSpeaker).arg(nSpeed).arg(nPitch).arg(0).arg(strTxt);
    QString strMd5Path = QString("%1%2.wav").arg(m_strDirTTS).arg(g_strGetMD5(strParams.toUtf8()));
    QFileInfo fi(strMd5Path);
    if( fi.isFile() ){
        slot_TtsPlayFile(strMd5Path);
        return;
    }
    // no file, create it
    int nLen = m_pAsrSDK->getTTSfile(strTxt, strMd5Path, strSpeaker, nSpeed, nPitch, 0);
    if( nLen < 46 ){
        QFile f(strMd5Path);
        f.remove();
        qDebug()<<"[Audio] make file error!"<<nLen<<strMd5Path;
        return;
    }
    // play
    slot_TtsPlayFile(strMd5Path);
}

void TmAudioWorker::slot_TtsPlay(QString strTxt)
{
    slot_TtsPlayParams(strTxt, m_tts_speaker, m_tts_speed, m_tts_pitch);
}

void TmAudioWorker::slot_TtsPlayFile(QString strPath)
{
    if(strPath.length()<1){
        return;
    }
    if( m_pTTSPlayer ){
        m_pTTSPlayer->setMedia(QUrl::fromLocalFile(strPath));
        m_pTTSPlayer->play();
    }
}

void TmAudioWorker::slot_TtsSetVolume(int nVal)
{
    if( m_pTTSPlayer ){
        m_pTTSPlayer->setVolume(nVal);
    }
}

void TmAudioWorker::slot_TtsStop()
{
    if( m_pTTSPlayer ){
        m_pTTSPlayer->stop();
    }
}

void TmAudioWorker::slot_AsrBuildCmd(QString strBnf)
{
    // !grammar tami;
    int nLeft = strBnf.indexOf("!grammar ");
    if( nLeft < 0 ){
        QString strJson = QString("{\"type\":\"error\",\"data\":{\"info\":\"bnf find grammar id start failed!\"}}");
        g_pRobotNotify->sendNotify("Robot", strJson.toUtf8().toStdString());
        return;
    }
    nLeft += 9; // skip head
    int nRight = strBnf.indexOf(";", nLeft);
    if( nRight < 0 ){
        QString strJson = QString("{\"type\":\"error\",\"data\":{\"info\":\"bnf find grammar id end failed!\"}}");
        g_pRobotNotify->sendNotify("Robot", strJson.toUtf8().toStdString());
        return;
    }
    QString strBnfName = strBnf.mid(nLeft, nRight-nLeft);
    // save file
    QFile bnfFile(QString("./msc/bnf/%1.bnf").arg(strBnfName));
    if( bnfFile.open(QIODevice::WriteOnly|QIODevice::Truncate) ){
        bnfFile.write(g_pCodecASCII->fromUnicode(strBnf));
        bnfFile.close();
    }
    // build
    if( !m_pAsrSDK->isr_build_bnf(strBnfName)){
        qDebug()<<"[Audio] isr_build_bnf build failed!";
        QString strJson = QString("{\"type\":\"error\",\"data\":{\"info\":\"bnf build grammar failed!\"}}");
        g_pRobotNotify->sendNotify("Robot", strJson.toUtf8().toStdString());
    }
}

void TmAudioWorker::slot_AsrSetGramma(QString name)
{
    m_pAsrSDK->isr_setGramID(name);
}

void TmAudioWorker::slot_AsrStart(int nMaxDelayMs,bool bIsOffline, bool bIsChinese)
{
    m_bIsOffline = bIsOffline;
    m_pAsrSDK->isr_start(bIsOffline,bIsChinese);
    m_nTimeStart = QTime::currentTime();
    m_nTimeEnd = m_nTimeStart.addMSecs(nMaxDelayMs);
    m_pAudioPCM = NULL;
    if( m_pcm_val>0 ){
        QString strTmp = QTime::currentTime().toString("hhmmss_zzz")+".pcm";
        m_pAudioPCM = fopen(strTmp.toLocal8Bit().data(), "wb");
    }
}

void TmAudioWorker::slot_AsrStop()
{
    QTime curTime = QTime::currentTime();
    if( curTime < m_nTimeEnd ){
        m_nTimeEnd = curTime;
    }
    if(!m_bIsOffline){
        m_bIsOffline = true;
        m_pAsrSDK->isr_stop();
        if( m_pAudioPCM ){
            fclose(m_pAudioPCM);
            m_pAudioPCM = NULL;
        }
    }
}

void TmAudioWorker::slot_asr_over_online(QString strResult)
{
    emit sig_asr_over(100,strResult);
}

void TmAudioWorker::slot_MediaPlay(QString strPath)
{
    if(strPath.length()<1){
        return;
    }
    if( m_pMediaPlayer ){
        m_pMediaPlayer->setMedia(QUrl::fromLocalFile(strPath));
        m_pMediaPlayer->play();
    }
}

void TmAudioWorker::slot_MediaSetVolume(int nVal)
{
    if( m_pMediaPlayer ){
        m_pMediaPlayer->setVolume(nVal);
    }
}

void TmAudioWorker::slot_MediaStop()
{
    if( m_pMediaPlayer ){
        m_pMediaPlayer->stop();
    }
}

void TmAudioWorker::slot_MicReset()
{
    if(m_bIsSerInit){
        QByteArray sendData = QString("RESET\n").toUtf8();
        m_pSerial->write(sendData);
    } 
}

void TmAudioWorker::slot_MicSet(int nVal)
{
    if(m_bIsSerInit){
        QByteArray sendData = QString("BEAM %1\n").arg(nVal).toUtf8();
        m_pSerial->write(sendData);
    }  
}

void TmAudioWorker::slot_audio_data()
{
    if( m_pAudioInput && m_pAudioDeviceIn ){
        QByteArray bArray = m_pAudioDeviceIn->readAll();
        _audio_data_in(bArray);
    }
}

void TmAudioWorker::_audio_data_in(QByteArray& bArray)
{
    //qDebug()<<"_audio_data_in:"<<bArray.size();
    if(!m_bIsOffline)
    {
        m_pAsrSDK->isr_updateAudio(bArray.data(), bArray.length());
        if(m_pAudioPCM){
            fwrite(bArray.data(), 1, bArray.length(), m_pAudioPCM);
        }
        return;
    }
    static bool bIsStop = true;
    QTime curTime = QTime::currentTime();
    if( curTime >= m_nTimeEnd ){
        if( !bIsStop ){
            _asr_get_result();
            if( m_pAudioPCM ){
                fclose(m_pAudioPCM);
                m_pAudioPCM = NULL;
            }
        }
        bIsStop = true;
        return;
    }
    if( curTime >= m_nTimeStart){
        bIsStop = false;
        m_pAsrSDK->isr_updateAudio(bArray.data(), bArray.length());
        if(m_pAudioPCM){
            fwrite(bArray.data(), 1, bArray.length(), m_pAudioPCM);
        }
    }
}

void TmAudioWorker::_asr_get_result()
{

    QString strResultXML = m_pAsrSDK->isr_getResult();
    if( m_pcm_val>0 ){
        qDebug()<<"ASR Result:"<<strResultXML;
    }
    m_pAsrSDK->isr_stop();
    int nConfidence = -1;
    QString strResultWord = "";

    if( strResultXML.length()>0 ){
        QDomDocument xmlDoc;
        QString strError;
        int nErrorLine, nErrorColum;
        if( !xmlDoc.setContent(strResultXML, &strError, &nErrorLine, &nErrorColum) ){
            qDebug()<< QString("Xml Parse Error! [%1:%2] %3").arg(nErrorLine).arg(nErrorColum).arg(strError);
            return;
        }
        // pase obj
        QDomElement root = xmlDoc.documentElement();
        if( root.isNull() ){
            //            qDebug()<<"xml Root is NULL!";
            return;
        }

        QDomNodeList nodelist = root.elementsByTagName("confidence");
        if( nodelist.length()<1 ){
            //            qDebug()<<"Nodelist is null";
            return;
        }
        QString strConfidence = nodelist.at(0).toElement().text();
        nConfidence = strConfidence.toInt(); // 返回可信度
        // result
        nodelist = root.elementsByTagName("result");
        if( nodelist.length()>0 ){
            nodelist = nodelist.at(0).toElement().elementsByTagName("object");
            if( nodelist.length()>0 ){
                QDomNodeList objList = nodelist.at(0).toElement().childNodes();
                QDomElement oneEle;
                for(int i=0; i<objList.size();i++){
                    oneEle = objList.at(i).toElement();
                    if( oneEle.tagName() == "ask" ){
                        strResultWord = oneEle.text();
                        break;
                    }
                }
            }

        }
    }
    qDebug()<<"[CmdMode]"<<nConfidence<<strResultWord;
    emit sig_asr_over(nConfidence, strResultWord);
}

TmAudioSender::TmAudioSender()
{
    m_pTimer = NULL;
    m_bIsOnline = false;
    m_bIsChinese = true;
    bAnswerEnable = false;
    m_pAudioWorker = new TmAudioWorker();
    g_pAsrAnswer = new TMAsrAnswer();

    m_pAudioTh = new QThread();
    m_pAudioWorker->moveToThread(m_pAudioTh);
    m_pAudioTh->start();

    m_pAsrTh = new QThread();
    g_pAsrAnswer->moveToThread(m_pAsrTh);
    m_pAsrTh->start();
    connect(this,SIGNAL(sig_getAsrAnswer(bool,QString)),g_pAsrAnswer,SLOT(slot_getAsrAnswer(bool,QString)));

    connect(this, SIGNAL(sig_Init()), m_pAudioWorker, SLOT(slot_Init()));
    connect(this, SIGNAL(sig_TtsPlayParams(QString,QString,int,int)), m_pAudioWorker, SLOT(slot_TtsPlayParams(QString,QString,int,int)));
    connect(this, SIGNAL(sig_TtsPlay(QString)), m_pAudioWorker, SLOT(slot_TtsPlay(QString)));
    connect(this, SIGNAL(sig_TtsSetVolume(int)), m_pAudioWorker, SLOT(slot_TtsSetVolume(int)));
    connect(this, SIGNAL(sig_TtsPlayFile(QString)), m_pAudioWorker, SLOT(slot_TtsPlayFile(QString)));
    connect(this, SIGNAL(sig_TtsStop()), m_pAudioWorker, SLOT(slot_TtsStop()));

    connect(this, SIGNAL(sig_AsrBuildCmd(QString)), m_pAudioWorker, SLOT(slot_AsrBuildCmd(QString)));
    connect(this, SIGNAL(sig_AsrSetGramma(QString)), m_pAudioWorker, SLOT(slot_AsrSetGramma(QString)));
    connect(this, SIGNAL(sig_AsrStart(int,bool, bool)), m_pAudioWorker, SLOT(slot_AsrStart(int,bool, bool)));
    connect(this, SIGNAL(sig_AsrStop()), m_pAudioWorker, SLOT(slot_AsrStop()));

    connect(this, SIGNAL(sig_MediaPlay(QString)), m_pAudioWorker, SLOT(slot_MediaPlay(QString)));
    connect(this, SIGNAL(sig_MediaSetVolume(int)), m_pAudioWorker, SLOT(slot_MediaSetVolume(int)));
    connect(this, SIGNAL(sig_MediaStop()), m_pAudioWorker, SLOT(slot_MediaStop()));

    connect(this, SIGNAL(sig_MicReset()), m_pAudioWorker, SLOT(slot_MicReset()));
    connect(this, SIGNAL(sig_MicSet(int)), m_pAudioWorker, SLOT(slot_MicSet(int)));

    connect(m_pAudioWorker, SIGNAL(sig_asr_over(int,QString)), this, SLOT(slot_asr_result(int,QString)), Qt::DirectConnection);

    m_nCount = 0;
    m_nTime = 30;
    mytask = new MyTimerTask();
    m_pTimer = new IceUtil::Timer();
    m_pTimer->scheduleRepeated(mytask, IceUtil::Time::seconds(1));   // 每隔1秒执行一次
    connect(&(*mytask),SIGNAL(sig_mic_reset()),this,SIGNAL(sig_MicReset()),Qt::DirectConnection);
}

void TmAudioSender::Init()
{
    emit sig_Init();
}

void TmAudioSender::TtsPlayParams(QString strTxt, QString strSpeaker, int nSpeed, int nPitch)
{
    emit sig_TtsPlayParams(strTxt, strSpeaker, nSpeed, nPitch);
}

void TmAudioSender::TtsPlay(QString strTxt)
{
    emit sig_TtsPlay(strTxt);
}

void TmAudioSender::TtsPlayFile(QString strPath)
{
    emit sig_TtsPlayFile(strPath);
}

void TmAudioSender::TtsSetVolume(int nVal)
{
    emit sig_TtsSetVolume(nVal);
}

void TmAudioSender::TtsStop()
{
    emit sig_TtsStop();
}

bool TmAudioSender::TtsIsPlaying()
{
    return (QMediaPlayer::PlayingState==m_pAudioWorker->m_pTTSPlayer->state());
}

void TmAudioSender::AsrBuild(QString strBnfText)
{
    emit sig_AsrBuildCmd(strBnfText);
}

void TmAudioSender::AsrSetGramma(QString name)
{
    emit sig_AsrSetGramma(name);
}

void TmAudioSender::AsrStart(int nMaxDelayMs, bool bIsOffline, bool bIsChinese)
{
    m_bIsOnline = !bIsOffline;
    m_bIsChinese = bIsChinese;
    emit sig_AsrStart(nMaxDelayMs,bIsOffline, bIsChinese);
}

void TmAudioSender::AsrStop()
{
    emit sig_AsrStop();
}

void TmAudioSender::MediaPlay(QString strPath)
{
    emit sig_MediaPlay(strPath);
}

void TmAudioSender::MediaSetVolume(int nVal)
{
    emit sig_MediaSetVolume(nVal);
}

void TmAudioSender::MediaStop()
{
    emit sig_MediaStop();
}

bool TmAudioSender::MediaIsPlaying()
{
    return (QMediaPlayer::PlayingState==m_pAudioWorker->m_pMediaPlayer->state());
}

void TmAudioSender::MicReset()
{
    emit sig_MicReset();
}

void TmAudioSender::MicSet(int nVal)
{
    emit sig_MicSet(nVal);
}

void TmAudioSender::AsrSetTimeOut(int nTimeDelayS)
{
    if(nTimeDelayS > 0){
        m_nTime = nTimeDelayS;
        if(!m_pTimer){
            mytask = new MyTimerTask();
            m_pTimer = new IceUtil::Timer();
            m_pTimer->scheduleRepeated(mytask, IceUtil::Time::seconds(1));   // 每隔1秒执行一次
            connect(&(*mytask),SIGNAL(sig_mic_reset()),this,SIGNAL(sig_MicReset()),Qt::DirectConnection);
        }

    }else{
        if(m_pTimer){
            m_pTimer->cancel(mytask);
            m_pTimer->destroy();
            m_pTimer = NULL;
        }
    }
}

void TmAudioSender::setExit()
{
    m_pAudioTh->terminate();
}

void TmAudioSender::slot_asr_result(int nConfidence, QString strResult)
{
    m_nCount = 0;
    if( g_pRobotNotify ){
        QString strJson = QString("{\"type\":\"asr\",\"data\":{\"confidence\": %1, \"result\":\"%2\",\"online\":%3}}")
                .arg(nConfidence).arg(strResult).arg(m_bIsOnline?"true":"false");
        g_pRobotNotify->sendNotify("Robot",std::string(strJson.toUtf8().data()));
    }
    if(bAnswerEnable)
        emit sig_getAsrAnswer(m_bIsChinese,strResult);
}

MyTimerTask::MyTimerTask(QObject *parent):QObject(parent)
{
}

void MyTimerTask::runTimerTask()
{
    if(g_pAudioSender->m_nCount == -1){
        return;
    }
    g_pAudioSender->m_nCount++;
    if(g_pAudioSender->m_nCount > g_pAudioSender->m_nTime){
        //g_pRobotNotify->sendNotify("Reset:","mic reset");
        emit sig_mic_reset();
        g_pAudioSender->m_nCount = -1;
    }
}
